#include "base/channel.h"
#include "base/eventloop.h"
#include "base/logfile.h"
#include "base/log.h"
#include <memory.h>
#include <atomic>

#include "base/logbuffer.h"
namespace net_tools::base
{
  namespace detail
  {
    struct Buffer_main
    {
      Buffer_main()
          : buffer{},
            now_buffer_index_(){};
      char buffer[_C::SET_LOG_BUFFER_SIZE];
      int now_buffer_index_;
    };
  }; // namespace detail

  Logbuffer::Logbuffer(Logfile *logfile, net_tools::Eventloop *eventloop)
      : logfile_(logfile),
        eventloop_(eventloop),
        fd_stream_(stdout),
        p_now_buffer_(new detail::Buffer_main()),
        p_flush_buffer_(new detail::Buffer_main()),
        is_swap_(false),
        mutex_(){};
  Logbuffer::~Logbuffer(){};
  void Logbuffer::Set_fd(FILE *fd_stream)
  {
    NT_LOG_INFO << "Change Log file" << NT_LOG_ENDL;
    new_fd_stream_ = fd_stream;
    need_change_file_ = true;
    Call_thread_to_flush();
  };
  size_t Logbuffer::Flush()
  {
    mutex_.Lock(); //锁入
    if (!is_swap_)
    {                                            //没换过区
      detail::Buffer_main *temp = p_now_buffer_; //换区
      p_now_buffer_ = p_flush_buffer_;
      p_flush_buffer_ = temp;
      is_swap_ = true;
    }
    if (need_change_file_) [[unlikely]]
    {
      fd_stream_ = new_fd_stream_;
    }
    mutex_.Unlock();
    size_t write_len = p_flush_buffer_->now_buffer_index_;
    size_t flush_len = fwrite_unlocked(p_flush_buffer_->buffer, 1, p_flush_buffer_->now_buffer_index_, fd_stream_);
    p_flush_buffer_->now_buffer_index_ -= flush_len;
    while (p_flush_buffer_->now_buffer_index_ > 0)
    {
      flush_len = fwrite_unlocked(p_flush_buffer_->buffer, 1, p_flush_buffer_->now_buffer_index_, fd_stream_);
      p_flush_buffer_->now_buffer_index_ -= flush_len;
      // bug here need jude error
    }
    is_swap_ = false; //don't need mutex 只有一个线程会flush
    fflush(fd_stream_);
    return write_len;
  };
  // 叫醒线程 要在锁内
  void Logbuffer::Call_thread_to_flush()
  {
    eventloop_->Run_in_right_thread([this]
                                    { logfile_->Flush(); });
  };
  void Logbuffer::Output(void *address, short int len)
  {
    mutex_.Lock();
    detail::Buffer_main *current_buffer = p_now_buffer_;
    int index = current_buffer->now_buffer_index_;
    current_buffer->now_buffer_index_ += len;
    if (current_buffer->now_buffer_index_ > _C::SET_LOG_HIGHLEVEL) [[unlikely]]
    { //现在这个区满了
      if (is_swap_) [[unlikely]]
      { //已经换区 无法换区
        current_buffer->now_buffer_index_ -= len;
        mutex_.Unlock();
        return;
      }
      else
      { //该区满了 则先把信息写入该区 然后换区 刷入
        memcpy(current_buffer->buffer + index, address, len);
        detail::Buffer_main *temp = current_buffer; //换区
        current_buffer = p_flush_buffer_;
        p_flush_buffer_ = temp;
        is_swap_ = true;
        Call_thread_to_flush();
        mutex_.Unlock();
        return;
      }
    }
    mutex_.Unlock();
    memcpy(current_buffer->buffer + index, address, len);
  };

} // namespace net_tools::base